﻿// Entity Framework MapTileStore - http://maptilestore.codeplex.com/
// Copyright © 2012 Majid Qaraee
// Licensed under the Microsoft Public License (Ms-PL)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace MapEntity.Model
{
    public class MapApi
    {
        private readonly double PixelTileSize = 256d;
        private readonly double DegreesToRadiansRatio = 180d / Math.PI;
        private readonly double RadiansToDegreesRatio = Math.PI / 180d;
        private readonly Point PixelGlobeCenter;
        private readonly double XPixelsToDegreesRatio;
        private readonly double YPixelsToRadiansRatio;
        private int _zoom;

        public MapApi(int zoomLevel)
        {
            _zoom = zoomLevel;
            var pixelGlobeSize = this.PixelTileSize * Math.Pow(2d, zoomLevel);
            this.XPixelsToDegreesRatio = pixelGlobeSize / 360d;
            this.YPixelsToRadiansRatio = pixelGlobeSize / (2d * Math.PI);
            var halfPixelGlobeSize = Convert.ToSingle(pixelGlobeSize / 2d);
            this.PixelGlobeCenter = new Point(
                halfPixelGlobeSize, halfPixelGlobeSize);
        }

        public int LongitudeToXPixel(double lng)
        {
            return (int)Math.Round(this.PixelGlobeCenter.X
                + (lng * this.XPixelsToDegreesRatio));
        }

        public int LongitudeToXTile(double lng)
        {
            return (int)((lng + 180.0) / 360.0 * (1 << _zoom));
        }

        public int LatitudeToYPixel(double lat)
        {
            var f = Math.Min(
                Math.Max(
                     Math.Sin(lat * RadiansToDegreesRatio),
                    -0.9999d),
                0.9999d);
            return (int)Math.Round(this.PixelGlobeCenter.Y + .5d *
                Math.Log((1d + f) / (1d - f)) * -this.YPixelsToRadiansRatio);
        }

        public int LatitudeToYTile(double lat)
        {
            return (int)((1.0 - Math.Log(Math.Tan(lat * Math.PI / 180.0) +
        1.0 / Math.Cos(lat * Math.PI / 180.0)) / Math.PI) / 2.0 * (1 << _zoom));
        }

        public Point FromCoordinatesToPixel(Point coordinates)
        {
            return new Point(
                LongitudeToXPixel(coordinates.X),
                LatitudeToYPixel(coordinates.Y));
        }

        public double XPixelToLongitude(int x)
        {
            return (x - this.PixelGlobeCenter.X) /
                this.XPixelsToDegreesRatio;
        }

        public double YPixelToLatitude(int y)
        {
            return (2 * Math.Atan(Math.Exp(
                (y - this.PixelGlobeCenter.Y) / -this.YPixelsToRadiansRatio))
                - Math.PI / 2) * DegreesToRadiansRatio;
        }

        public Point FromPixelToCoordinates(Point pixel)
        {
            return new Point(
                YPixelToLatitude((int)pixel.Y),
                XPixelToLongitude((int)pixel.X));
        }

        public Point Tile(double lat, double lng)
        {
            return new Point(LongitudeToXTile(lng) / this.PixelTileSize, LatitudeToYTile(lat));
        }
    }
}
